package com.atguigu.juc.aqs;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author yanglin
 * @create 2024-02-02 13:41 星期五
 * description:
 */
public class AQSDemo {


    /**
     * 总结：
     *      整个ReentrantLock的枷锁过程，可以分为三个阶段：
     *          1、尝试加锁
     *              tryAcquire方法尝试获取锁，以下几种情况会导致获取锁失败：
     *                  1、锁已经被其他线程获取
     *                  2、锁没有被其他线程获取，但当前线程需要排队
     *                  3、cas失败（可能过程中已经有其他线程拿到锁了）
     *                  锁为自由状态（state == 0），并不能说明可以立即执行cas获取锁，因为可能在当前线程获取锁之前，
     *                  已经有其他线程在排队了，必须遵循先来后到的原则获取锁，所以还要调用hasQueuedPredecessors方法，
     *                  查看自己是否需要排队
     *          2、加锁失败，线程入队列
     *              将当前线程封装成Node对象，并加入排队队列中
     *              根据排队队列是否执行过初始化，执行1、2不同处理逻辑
     *              1、如果排队队列不为空，即之前已经初始化过了，此时只需要将新的node加入到排队队列末尾即可
     *              2、如果排队队列为空，需要执行队列初始化，enq会初始化一个空的Node，作为排队队列的head，然后将需要排队的
     *              线程作为head的next节点插入
     *          3、线程入队列后，进入阻塞状态
     */
    
    
    public static void main(String[] args) {
        //非公平锁
        ReentrantLock lock = new ReentrantLock();
        //A、B、C三个顾客，去银行办理业务，A先到，此时窗口空无一人，他优先获得办理窗口的机会，办理业务
        //A耗时严重，估计长期占有窗口
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("Thread A come in");
                //模拟办理业务，暂停20分钟
                try {
                    TimeUnit.MINUTES.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
            }finally {
                //无论正常执行还是发生异常，都需要解锁
                lock.unlock();
            }
        },"A").start();
        
        //B是第二个顾客，B一看到手里窗口被A占用，只能去候客区等待，进入AQS队列，等待着A办理完成，尝试去抢占受理窗口
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("Thread B come in");
            }finally {
                lock.unlock();
            }
        },"B").start();
        
        //C 是第三个顾客，C一看到受理窗口被A占用，只能去候客区等待，进入到AQS队列，等待A办理完成，尝试去抢占窗口，前面是B顾客，FIFO（first in first out）
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("Thread C come in");
            }finally {
                lock.unlock();
            }
        },"C").start();
        
        //
    }
    
}
